﻿using System;
using System.Collections.Generic;
using System.Text;
using Atosenet.PluginEngine;

namespace Atosenet.Protocol
{
    [PluginAttribute("Atosenet", "Atosenet.Protocol.ProtocolPacketEDU", "ATOS教育平台协议", PluginType.Protocol)]
    public class ProtocolPacketEDU : ProtocolPacket
    {
        #region 成员

        public const byte ASP_SOP = 0x37;

        public const int ASP_SOP_LEN = 0x01;
        public const int ASP_CMD_LEN = 0x02;
        public const int ASP_LEN_LEN = 0x02;
        public const int ASP_FCS_LEN = 0x01;
        public const int ASP_HEADER_LEN = ASP_SOP_LEN + ASP_CMD_LEN + ASP_LEN_LEN;


        private int count_header = 0; //当前已存包头长度
        private int count_data = 0; //当前已存数据长度

        private byte[] buffer_header;    //包头缓冲区
        private byte[] buffer_data;      //包内数据缓冲区

        private bool header_ok = false;
        private bool data_ok = false;
        private bool fcs_ok = false;

        private bool valid_header = false;
        private bool valid_data = false;

        private int index_sop = 0;
        private int index_cmd = 0;
        private int index_len = 0;

        private EDUPacketState state = EDUPacketState.ASPPS_NONE;

        private UInt16 len = 0;
        private DateTime _receiveTime;

        #endregion


        #region 初始化

        public ProtocolPacketEDU()
        { }

        #endregion


        #region 公开函数

        public override void Append(byte[] data, ref int offset)
        {
            bool bDone = false;

            do
            {
                bDone = true; //默认只会处理一次

                switch (state)
                {
                    case EDUPacketState.ASPPS_NONE:
                        {
                            if (getHeader(data, ref offset) >= 0)
                            {//此数据包含包头，进入包头接收状态
                                createHeader();
                                state = EDUPacketState.ASPPS_HEADER;
                                bDone = false;
                            }
                        }
                        break;
                    case EDUPacketState.ASPPS_HEADER:
                        {//正在分析包头
                            appendHeader(data, ref offset);

                            if (header_ok)
                            {//如果数据包头已经完整，则分析包头
                                parseHeader();
                                if (!valid_header)
                                {//包头数据出错
                                    state = EDUPacketState.ASPPS_ERROR;
                                    return;
                                }

                                createData();
                                state = EDUPacketState.ASPPS_DATA;
                            }

                            bDone = (offset >= data.Length);
                        }
                        break;

                    case EDUPacketState.ASPPS_DATA:
                        {//正在分析包内数据
                            appendData(data, ref offset);

                            if (data_ok)
                            {//数据分析完毕，进入FCS
                                state = EDUPacketState.ASPPS_FCS;
                            }

                            bDone = (offset >= data.Length);
                        }
                        break;
                    case EDUPacketState.ASPPS_FCS:
                        {
                            appendFCS(data, ref offset);

                            if (fcs_ok)
                            {//FCS接收完毕，整个数据接收完成
                                state = EDUPacketState.ASPPS_END;
                                _receiveTime = DateTime.Now;
                            }
                        }
                        break;
                }
            } while (!bDone);
        }

        public override byte[] Generate()
        {
            //检查LEN与DATA是否对应
            if (buffer_data == null || LEN != buffer_data.Length)
                return null;

            //数据包总长度　＝　包头长度　＋　数据长度　＋　校验码长度
            int nPacketLength = ASP_HEADER_LEN + LEN + ASP_FCS_LEN;
            int nIndex = 0;
            int nLen = 0;
            byte[] aData = new byte[nPacketLength];

            //加入包头
            generateHeader();
            nLen = ASP_HEADER_LEN;
            Array.Copy(buffer_header, 0, aData, nIndex, nLen);
            nIndex += nLen;

            //加入包内数据
            nLen = LEN;
            Array.Copy(buffer_data, 0, aData, nIndex, nLen);
            nIndex += nLen;

            //加入FCS            
            FCS = calcFCS();
            aData[nIndex] = FCS;
            return aData;
        }

        public override void Reset()
        {
            count_data = 0;
            count_header = 0;
            buffer_data = new byte[0];
            buffer_header = new byte[0];
            header_ok = false;
            data_ok = false;
            fcs_ok = false;
            valid_data = false;
            valid_header = false;
            index_sop = 0;
            index_cmd = 0;
            index_len = 0;
            state = EDUPacketState.ASPPS_NONE;
            len = 0;
        }

        public override object Clone()
        {
            ProtocolPacketEDU packet = ProtocolPacketEDU.CreatePacket(this.CMD, this.DATA);
            packet._receiveTime = Timestamp;
            return packet;
        }

        public override string ToString()
        {
            return string.Format("EDU Protocol Packet{0}([SOP]={1},[CMD]={2},[LEN]={3},[DATA]={4},[PCS]={5})",
                Environment.NewLine,
                ASP_SOP.ToString("X2"),
                CMD.ToString("X2"),
                LEN.ToString("X2"),
                Converter.ConvertToHexString(DATA),
                FCS.ToString("X2"));
        }

        #endregion


        #region 静态函数

        public static ProtocolPacketEDU CreatePacket(UInt16 fieldCmd, byte[] fieldData)
        {
            ProtocolPacketEDU packet = new ProtocolPacketEDU();
            packet.CMD = fieldCmd;
            packet.DATA = fieldData;
            byte[] data = packet.Generate();
            return ProtocolPacketEDU.CreatePacket(data);
        }

        public static ProtocolPacketEDU CreatePacket(UInt16 fieldCmd)
        {
            ProtocolPacketEDU packet = new ProtocolPacketEDU();
            packet.CMD = fieldCmd;
            packet.DATA = new byte[] { };
            packet.Generate();
            byte[] data = packet.Generate();
            return ProtocolPacketEDU.CreatePacket(data);
        }

        public static ProtocolPacketEDU CreatePacket(byte[] rawData)
        {
            ProtocolPacketEDU packet = new ProtocolPacketEDU();
            int offset = 0;
            packet.Append(rawData, ref offset);
            return packet;
        }

        public static ProtocolPacketEDU CreatePacket()
        {
            ProtocolPacketEDU packet = new ProtocolPacketEDU();
            return packet;
        }

        #endregion


        #region 核心函数

        private bool isValidOffset(byte[] data, int offset)
        {
            return ((offset >= 0) && (offset < data.Length));
        }

        private int findData(byte[] data, byte toFind, ref int offset)
        {//在缓冲区中查找相应的字符
            if (data == null) return -1;

            for (; offset < data.Length; offset++)
            {
                if (data[offset] == toFind)
                {
                    return offset;
                }
            }

            return -1;
        }
        private int findData(byte[] data, byte[] toFind, ref int offset)
        {//在缓冲区中查找相应的数据段
            if (data == null) return -1;

            for (; offset < data.Length; offset++)
            {
                int j = 0;
                for (; j < toFind.Length; j++)
                {
                    if (data[offset] != toFind[j]) break;
                }

                if (j >= toFind.Length) return 1;
            }

            return -1;
        }

        private byte[] getData(byte[] data, int from, int length)
        {//取得某一段的数据
            int nLeftLength = data.Length - from;
            int nValidLength = (nLeftLength < length) ? nLeftLength : length;

            if (nValidLength <= 0) return new byte[0];  //所要取的数据不存在

            byte[] aSubData = new byte[nValidLength];

            int n = 0;

            for (int i = 0; i < nValidLength; i++)
            {
                aSubData[n++] = data[(from + i)];
            }

            return aSubData;
        }

        private int findSOP(byte[] data, ref int offset)
        {//查找包头
            return findData(data, ASP_SOP, ref offset);
        }

        public byte calcFCS()
        {//计算出数据生成对应的FCS
            //int j = 0;
            byte nFCS = (byte)(buffer_header[1]); //跳过SOP，从SN的第一字节开始

            for (int i = 2; i < buffer_header.Length; i++)
            {
                nFCS ^= (byte)(buffer_header[i]);
            }

            for (int i = 0; i < buffer_data.Length; i++)
            {
                nFCS ^= (byte)(buffer_data[i]);
            }

            return nFCS;
        }

        private bool createHeader()
        {
            buffer_header = new byte[ASP_HEADER_LEN];
            count_header = 0;

            return true;
        }
        private bool createData()
        {//初始化包内数据缓冲区，准备接收包内数据
            if (LEN < 0) return false;

            buffer_data = new byte[LEN];
            count_data = 0;

            return true;
        }

        public bool checkFCS()
        {//检验数据库包中的FCS是否与
            return (FCS == calcFCS());
        }

        private bool parseHeader()
        {//解析包头
            byte[] data = buffer_header;
            valid_header = false;

            int nIndex = 0; //目前读取到的位置
            int nLen = 0;   //每次getData的长度
            
            //查找包头            
            nLen = ASP_SOP_LEN;
            nIndex = findSOP(data, ref nIndex);

            if (nIndex == -1) 
                return false;

            index_sop = nIndex;
            nIndex += nLen;

            //取得CMD
            nLen = ASP_CMD_LEN;
            index_cmd = nIndex;
            CMD = Converter.ConvertToUInt16BE(data, nIndex);
            nIndex += nLen;

            //取得LEN
            nLen = ASP_LEN_LEN;
            index_len = nIndex;
            len = Converter.ConvertToUInt16BE(data, nIndex);
            nIndex += nLen;

            valid_header = true;
            return valid_header;
        }

        private int getHeader(byte[] data, ref int offset)
        {//从数据段中取出后面包含包头的数据部分（可能包含多余的数据）
            return findSOP(data, ref offset);
        }

        private bool appendHeader(byte[] data, ref int offset)
        {//添加数据到包头
            if (count_header >= ASP_HEADER_LEN)
            {
                header_ok = true;
                return true;
            }

            while (offset < data.Length)
            {
                buffer_header[count_header++] = data[offset++];
                if (count_header >= ASP_HEADER_LEN)
                {
                    header_ok = true;
                    return true;
                }
            }

            return false;
        }

        private bool appendData(byte[] data, ref int offset)
        {//增加包内数据
            if (count_data >= LEN)
            {
                data_ok = true;
                return true;
            }

            while (offset < data.Length)
            {
                buffer_data[count_data++] = data[offset++];
                if (count_data >= LEN)
                {
                    data_ok = true;
                    return true;
                }
            }

            return false;
        }

        private bool appendFCS(byte[] data, ref int offset)
        {//增加检验码

            if (offset < data.Length)
            {
                FCS = data[offset++];
                fcs_ok = true;
                return true;
            }
            return false;
        }

        #endregion

        #region 生成包时用的函数

        private bool generateHeader()
        {//用SOP，SN，CMD，LEN生成对应的包头
            createHeader();

            int nIndex = 0;
            int nLen = ASP_SOP_LEN;
            byte[] aData;

            //生成SOP
            buffer_header[nIndex] = ASP_SOP;
            nIndex += nLen;

            //生成CMD
            nLen = ASP_CMD_LEN;
            aData = Converter.ConvertToByteArrayBE(CMD);
            Array.Copy(aData, 0, buffer_header, nIndex, nLen);
            nIndex += nLen;

            //生成LEN
            nLen = ASP_LEN_LEN;
            aData = Converter.ConvertToByteArrayBE(LEN);
            Array.Copy(aData, 0, buffer_header, nIndex, nLen);
            nIndex += nLen;

            return true;
        }
        
        private void ResetState()
        {//重设至初始状态，将放弃之前的所有数据
            state = EDUPacketState.ASPPS_NONE;
            header_ok = false;
            data_ok = false;
            fcs_ok = false;
            count_header = 0;
            count_data = 0;
            valid_header = false;
            valid_data = false;
        }

        #endregion


        #region 属性

        public override byte[] PacketFlag
        {
            get { return new byte[] { ASP_SOP }; }
        }

        public override DateTime Timestamp
        {
            get { return _receiveTime; }
        }

        public override bool IsCompleted
        {
            get { return state == EDUPacketState.ASPPS_END; }
        }

        public override string PacketDataHex
        {
            get { return Converter.ConvertToHexString(RawData); }
        }

        public override bool IsValid
        {
            get
            {
                if (!header_ok || !data_ok || !fcs_ok)
                    return false;

                valid_data = checkFCS();
                return valid_header && valid_data;
            }
        }

        public override bool HasError
        {
            get { return state == EDUPacketState.ASPPS_ERROR; }
        }

        public override byte[] RawData
        {
            get
            {
                byte[] data = Generate();
                return data;
            }
        }

        public UInt16 LEN
        {
            get
            {
                return len;
            }
            set
            {
                if (len != value || len == 0)
                {
                    len = value;
                    createData();
                }
            }
        }

        public byte[] DATA
        {
            get
            {
                if (LEN == 0)
                    return new byte[0];  //返回空数据组

                return buffer_data;
            }
            set
            {
                if (value == null)
                {
                    buffer_data = null;
                }
                else
                {
                    LEN = (UInt16)value.Length;
                    createData();
                    value.CopyTo(buffer_data, 0);
                }
            }
        }

        public byte FCS
        {
            get;
            set;
        }

        public UInt16 CMD
        {
            get;
            set;
        }

        #endregion


        #region 事件



        #endregion
    }


    internal enum EDUPacketState
    {
        ASPPS_NONE = 0,   //未进入接收包数据状态
        ASPPS_HEADER = 1, //正在接收包头
        ASPPS_DATA = 2,   //正在读取包内数据
        ASPPS_FCS = 3,    //正在接收包验证信息FCS
        ASPPS_END = 4,   //已经接收一个完整的数据包
        ASPPS_ERROR = 5   //Error Occurs
    }
}
